
  if (DEBUG) console.log('In utils.js');
//Data storage for pages
var gPageData = {};
//Object of current page
var gLastScriptLoaded = {};
//Object of current page
var gLoadedScriptElements = {};
//Object of current page
var gLoadedScriptObjects = {};
//Object of current page
var gCurrentPage = {};
//DOM element representing the current page script
var gCurrentPageScriptElement = null;
//Function called when a script is loaded
var gScriptLoaded = function(){};
//Page TItle
var gPageTitle = 'MythTV';
//Back link Title
var gBackLinkText = '';
//Page Template
var gPageTemplate = null;
//Menu Template
var gMenuItemTemplate = null;
//Debug Global
var DEBUG = true && typeof (console) != 'undefined';
// Whether using telescope mode (true) or wings mode (false);
var gTelescope = true;

//Onload Event Handler
$(document).ready(function () {
    
//webkit css file    
    if (navigator.userAgent.indexOf('WebKit') != -1)
    {
          var fileref=document.createElement("link");
          fileref.setAttribute("rel", "stylesheet");
          fileref.setAttribute("type", "text/css");
          fileref.setAttribute("href", SkinDir + 'webkit.css');
          var head = document.getElementsByTagName("head")[0];
          head.appendChild(fileref);
    }
    
		//load default page
	$.ajax( 
	{
		url: SkinDir + 'defaultpage.html',
		type: 'GET',
		error: function(transport) 
		{  
			alert('default page not loaded!');
			showError('Error downloading required files (defaultpage.html): ' + transport.responseText);
		},
		success: function(response)
		{ 
			gPageTemplate = new Template();
			gPageTemplate.load(response);
			gMenuItemTemplate = gPageTemplate.removeSubTemplate('menu_item');
			
			$.history.init(PageChange);
	
		}
	});
});
     
//Load javascript file function
function LoadFile(fileURL,callback)
{      
	if (DEBUG) console.log('Load file: ' + fileURL);
    return $.ajax(
        { 
			url: fileURL,
            method: 'GET',
            success: callback,
            error: function(transport, textStatus, errorThrown) {  
				showError("Error retrieving file: " + fileURL + " response: " + textStatus);
            }
        });
}

//display an error message
function showError(msg)
{
    if (DEBUG) console.error(msg);
    var el = document.getElementById("errorBox");
    el.style.display = "block";
    var el2 = document.getElementById("errorMsg");
    el2.innerHTML = msg;
}
    
//Loads a script (or css file) by adding it to the DOM, returns the element representing this file
function LoadScript(filename, filetype)
{
	//See if already loaded
	if (gLoadedScriptElements[filename] != undefined)
		return gLoadedScriptElements[filename];
		
     if (filetype=="js"){ //if filename is a external JavaScript file
      var fileref=document.createElement('script');
      fileref.setAttribute("type","text/javascript");
      fileref.setAttribute("src", filename);
     }
     else if (filetype=="css"){ //if filename is an external CSS file
      var fileref=document.createElement("link");
      fileref.setAttribute("rel", "stylesheet");
      fileref.setAttribute("type", "text/css");
      fileref.setAttribute("href", filename);
     }
     else
        return;  //cannot handle other files yet
        
     if (typeof fileref!="undefined")
     {
        var head = document.getElementsByTagName("head")[0];
        head.appendChild(fileref);
		gLoadedScriptElements[filename] = fileref;
     }
     
     return fileref;
}
//Unloads a script or css file by removing it's element from the DOM
function UnloadScript(scriptElement)
{
    var head = document.getElementsByTagName("head")[0];
    head.removeChild(scriptElement);
	for (var i in gLoadedScriptElements) 
		if (gLoadedScriptElements[i] == scriptElement) delete gLoadedScriptElements[i]; 
}
/*
 * Shows a panel page
 */
function ShowPanel(title,content)
{
	if (title == null)
		title = gPageTitle;
	
	if (title != '')
	{
		document.getElementById('pageTitle').innerHTML = title;
	}
		
    //update content
	var tpl = GetPanelTemplate;
	tpl.setData({ 'content': content});
	
    document.getElementById('content').innerHTML = tpl.Parse();
}

//Processes the output of the page scripts, adds default page title section, showbackbutton is optional
function ProcessPageOutput(title,content,type,showBackButton)
{
    if (!type) var type = '';
    
    //cut page title
    var pagetitle = title
    if (pagetitle.length > 25)
        pagetitle = pagetitle.substr(0,22) + '...';
    
    var contentEl = document.getElementById('content');
    if (contentEl)
    {
        contentEl.innerHTML = content.parse();
        document.getElementById('pageTitle').innerHTML = pagetitle;
		document.getElementById('backButton').style.display = (showBackButton ? 'inline' : 'none');
        document.title = title;
        
    }
    else
    {   //Not current showing default page template, load in again   
		var backButtonStyle = (showBackButton ? 'display: inline;' : 'display: none;');
        var tpl = gPageTemplate;
        tpl.addData({'title': pagetitle, 'content': content, 'type': type, 'backButtonStyle': backButtonStyle});
     
        ProcessFullPageOutput(title,tpl);
    }
    
}

//For more complex pages allow to change the full body content
function ProcessFullPageOutput(title,tpl)
{
    document.getElementsByTagName('body')[0].innerHTML = tpl.parse();
	
    //set title
    document.title = title;
}

//Loads a new page
function PageChange(newlocation,historyData)
{
	if (DEBUG) console.log('In PageChange');
    //Extract page name  
    if (typeof(newlocation) == undefined) throw {name:'UNDEFINED'};
    var i = newlocation.indexOf("?");
    var page = ((i >= 0)
            ? newlocation.substr(0,i)
            : newlocation
        );
    var queryString = ((i >= 0)
            ? newlocation.substr(i+1)
            : ''
        );      
        
    //remove any extension
    i = page.indexOf(".");
    page = (i >= 0) ? page.substr(0,i) : page;
    
    //get default page if not set
    if (page == '') page = 'main';
    
	//decode parameters into params object
    var params = {};
    var regex = /([a-zA-Z0-9$\-_.+!*'(),%]+)=([a-zA-Z0-9$\-_.+!*'(),% ]+)(&|$)/;
    var match = queryString.match(regex);
    while(match)
    {
        params[Url.decode(match[1])] = Url.decode(match[2]);
        queryString = queryString.replace(match[0],'');
        match = queryString.match(regex);
    }
    
    //create a PageData variable and load in values
    //if (typeof gPageData[page] == 'undefined') 
	gPageData[page] = {};
    for (var paramName in params)
    {
        gPageData[page][paramName] = params[paramName];
    }
    
    //This is required for the page to be executed again when the script is loaded
    if (!(gCurrentPageScriptElement == null))
        UnloadScript(gCurrentPageScriptElement);
    
    //callback when page is loaded
    gScriptLoaded = function() { if (gCurrentPage.unloadPage != undefined) gCurrentPage.unloadPage(); gCurrentPage = gLastScriptLoaded; gCurrentPage.loadPage(); window.scrollTo(0,1); gScriptLoaded = function(){}; };

    if (DEBUG) console.log('Loading Script: ' + SkinDir + page + '.js');
    gCurrentPageScriptElement = LoadScript(SkinDir + page + '.js','js');
}
  if (DEBUG) console.log('After PageChange');
//Loads a new page and adds a history event
function GoToPage(pagename)
{
    if (DEBUG) console.log('Going to page: ' + pagename);
	$.history.load(pagename);
}

//Processes a link element
function ProcessLink(el)
{
    if (DEBUG) console.log('Processing Link to: ' + el.href);
	if (el['href'] == undefined) return true;
	
	var href = el.href;
	var i = href.indexOf('#');
	if (i > -1)
	{
		GoToPage(href.substring(i+1));
		return false;
	}
	else
		return true;
}
	

//Updates page without calling it a new history item (TODO: FIXME: make this work!)
function UpdatePage(pagename)
{
    //buggy as hell, ignore
    //dhtmlHistory.update(pagename,{});
    dhtmlHistory.add(pagename,{});
    PageChange(pagename,{});
}

//gets current date and time in GMT timezone
function GMT()
{
    var time = new Date();
    time.setTime(time.getTime() + time.getTimezoneOffset()*60000);
    return time;
}

//Loads the panel template as required
var gPanelTemplateStr = null;
function GetPanelTemplate()
{
	if (gPanelTemplateStr == null)
	{
		new Ajax.Request(SkinDir + 'panelTemplate.html',
			{ 
				method: 'get',
				asynchronous: false,
				onFailure: function(transport) {  
				showError( (DEBUG == true ? "Error retrieving file: " + fileURL + " response: " : '') + transport.responseText);
				},
				onSuccess:	function(t)
				{ gPanelTemplateStr = t.responseText; }
			});
	}
	var tpl = new Template();
	tpl.load(gPanelTemplateStr);
	return tpl;
}
//Adds default page data
function AddDefaultPageData(page,data)
{
    if (typeof gPageData[page] == 'undefined')
    {
        gPageData[page] = data;
        return;
    }
    else
    {
        for (var dataName in data)
        {
            if (typeof gPageData[page][dataName] == 'undefined')
                gPageData[page][dataName] = data[dataName];
        }
    }
}
/**
*
*  URL encode / decode
*  http://www.webtoolkit.info/
*
**/

var Url = {
 
	// public method for url encoding
	encode : function (string) {
		return escape(this._utf8_encode(string));
	},
 
	// public method for url decoding
	decode : function (string) {
		return this._utf8_decode(unescape(string));
	},
 
	// private method for UTF-8 encoding
	_utf8_encode : function (string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";
 
		for (var n = 0; n < string.length; n++) {
 
			var c = string.charCodeAt(n);
 
			if (c < 128) {
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}
 
		}
 
		return utftext;
	},
 
	// private method for UTF-8 decoding
	_utf8_decode : function (utftext) {
		var string = "";
		var i = 0;
		var c1 =0;
		var c2 = 0;
		var c = 0;
 
		while ( i < utftext.length ) {
 
			c = utftext.charCodeAt(i);
 
			if (c < 128) {
				string += String.fromCharCode(c);
				i++;
			}
			else if((c > 191) && (c < 224)) {
				c2 = utftext.charCodeAt(i+1);
				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
				i += 2;
			}
			else {
				c2 = utftext.charCodeAt(i+1);
				c3 = utftext.charCodeAt(i+2);
				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
				i += 3;
			}
 
		}
 
		return string;
	}
 
}

function FormatDate(date,formatstr)
{
    formatstr = formatstr.replace("Y",date.getFullYear());
    formatstr = formatstr.replace("m",((date.getMonth() < 9) ? '0' : '') + (date.getMonth()+1));
    formatstr = formatstr.replace("d",((date.getDate() < 9) ? '0' : '') + (date.getDate()));
    formatstr = formatstr.replace("H",((date.getHours() < 10) ? '0' : '') + (date.getHours()));
    formatstr = formatstr.replace("g",((date.getHours() > 12) ? date.getHours() - 12 :date.getHours())); //12 hr, no leading zeros
    formatstr = formatstr.replace("a",((date.getHours() > 12) ? 'pm' : 'am')); //12 hr, no leading zeros
    formatstr = formatstr.replace("i",((date.getMinutes() < 10) ? '0' : '') + (date.getMinutes()));
    formatstr = formatstr.replace("s",((date.getSeconds() < 10) ? '0' : '') + (date.getSeconds()));
    return formatstr;
}
// Simulates PHP's date function
Date.prototype.format = function(format) {
	var returnStr = '';
	var replace = Date.replaceChars;
	for (var i = 0; i < format.length; i++) {
		var curChar = format.charAt(i);
		if (replace[curChar]) {
			returnStr += replace[curChar].call(this);
		} else {
			returnStr += curChar;
		}
	}
	return returnStr;
};
Date.replaceChars = {
	shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
	longMonths: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
	shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
	longDays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
	
	// Day
	d: function() { return (this.getDate() < 10 ? '0' : '') + this.getDate(); },
	D: function() { return Date.replaceChars.shortDays[this.getDay()]; },
	j: function() { return this.getDate(); },
	l: function() { return Date.replaceChars.longDays[this.getDay()]; },
	N: function() { return this.getDay() + 1; },
	S: function() { return (this.getDate() % 10 == 1 && this.getDate() != 11 ? 'st' : (this.getDate() % 10 == 2 && this.getDate() != 12 ? 'nd' : (this.getDate() % 10 == 3 && this.getDate() != 13 ? 'rd' : 'th'))); },
	w: function() { return this.getDay(); },
	z: function() { return "Not Yet Supported"; },
	// Week
	W: function() { return "Not Yet Supported"; },
	// Month
	F: function() { return Date.replaceChars.longMonths[this.getMonth()]; },
	m: function() { return (this.getMonth() < 9 ? '0' : '') + (this.getMonth() + 1); },
	M: function() { return Date.replaceChars.shortMonths[this.getMonth()]; },
	n: function() { return this.getMonth() + 1; },
	t: function() { return "Not Yet Supported"; },
	// Year
	L: function() { return "Not Yet Supported"; },
	o: function() { return "Not Supported"; },
	Y: function() { return this.getFullYear(); },
	y: function() { return ('' + this.getFullYear()).substr(2); },
	// Time
	a: function() { return this.getHours() < 12 ? 'am' : 'pm'; },
	A: function() { return this.getHours() < 12 ? 'AM' : 'PM'; },
	B: function() { return "Not Yet Supported"; },
	g: function() { return this.getHours() % 12 || 12; },
	G: function() { return this.getHours(); },
	h: function() { return ((this.getHours() % 12 || 12) < 10 ? '0' : '') + (this.getHours() % 12 || 12); },
	H: function() { return (this.getHours() < 10 ? '0' : '') + this.getHours(); },
	i: function() { return (this.getMinutes() < 10 ? '0' : '') + this.getMinutes(); },
	s: function() { return (this.getSeconds() < 10 ? '0' : '') + this.getSeconds(); },
	// Timezone
	e: function() { return "Not Yet Supported"; },
	I: function() { return "Not Supported"; },
	O: function() { return (-this.getTimezoneOffset() < 0 ? '-' : '+') + (Math.abs(this.getTimezoneOffset() / 60) < 10 ? '0' : '') + (Math.abs(this.getTimezoneOffset() / 60)) + '00'; },
	T: function() { var m = this.getMonth(); this.setMonth(0); var result = this.toTimeString().replace(/^.+ \(?([^\)]+)\)?$/, '$1'); this.setMonth(m); return result;},
	Z: function() { return -this.getTimezoneOffset() * 60; },
	// Full Date/Time
	c: function() { return "Not Yet Supported"; },
	r: function() { return this.toString(); },
	U: function() { return this.getTime() / 1000; }
};

function deserializeDate(date)
{
    //Deserailises a date in YYYYmmddhhmmss format
    var year = parseInt(date.substr(0,4),10);
    var month = parseInt(date.substr(4,2),10);
    var day = parseInt(date.substr(6,2),10);
    var hour = parseInt(date.substr(8,2),10);
    var minute = parseInt(date.substr(10,2),10);
    //Seconds can be optional
    var second = (date.length >= 14) ? parseInt(date.substr(12,2),10) : 0;

    date = new Date();
    date.setYear(year);
    date.setMonth(month-1);
    date.setDate(day);
    date.setHours(hour);
    date.setMinutes(minute);
    date.setSeconds(second);
    date.setMilliseconds(0);
    return date;
}

function CreateMediaItemMenu(menu_items,item)
{
	var itemsTplList = [];
	var linkTpl = new Template();
	var itemTpl = gMenuItemTemplate.clone();
	itemTpl.addSubTemplate('link',linkTpl);
	for (var i=0;i<menu_items.length;i++)
	{
		//the link gets it own subtemplate so the item data can be parsed into it
		linkTpl.load(menu_items[i].URL);
		itemTpl.addData({
			name: item.name,
			artist: item.artist,
			album: item.album,
			guid: item.guid,
			genre: item.genre,
			label: menu_items[i].label,
			imgURL: menu_items[i].imgURL});
		itemsTplList.push(itemTpl.clone());
	}
	//Add cancel button
	var cancelTpl = new Template('<li><a href="" onclick="HideMenu(); return false;" class="whiteButton" >Cancel</a></li>');
	itemsTplList.push(cancelTpl);
	//Create an outer template to handle parsing of the list
	var listTpl = new Template();
	listTpl.load('<< #list >>');
	listTpl.addSubTemplate('list',itemsTplList);
	
	document.getElementById('link_menu_list').innerHTML = listTpl.parse();
}

function ShowMenu()
{
	var el = document.getElementById('link_menu_container');
	el.ontouchmove = function (eventObj) { eventObj.preventDefault(); };
	//set it so the bottom of the 
	var ScrollTop = document.body.scrollTop;
	if (ScrollTop == 0)
	{
		if (window.pageYOffset)
			ScrollTop = window.pageYOffset;
		else
			ScrollTop = (document.body.parentElement) ? document.body.parentElement.scrollTop : 0;
	}
	
	var offset = ScrollTop;
	el.style.top = offset;
	el.style.width = window.innerWidth + 'px';
	el.style.height = window.innerHeight + 'px';
	el.style.display = 'block';
}

function HideMenu()
{
	var el = document.getElementById('link_menu_container');
	el.style.display = 'none';
}

//client-side search by filtering a list
function LocalSearch(list,fields,searchTerm)
{
	if (searchTerm == '')
		return list;
		
	var matches = [];
	var searchTermRE = RegExp.escape(searchTerm)
	var reg = new RegExp(RegExp.escape(searchTerm),'gi');
	
	if (typeof(field) == 'string')
		fields = [field];
	for (var i=0;i<list.length;i++)
	{
		for (var j=0;j<fields.length;j++)
		{
			if (typeof(list[i][fields[j]]) == 'string' && list[i][fields[j]].search(reg) > -1)
			{
				matches.push(list[i]);
				break;
			}
		}
	}
	return matches;
}

RegExp.escape = function(text) {
  if (!arguments.callee.sRE) {
    var specials = [
      '/', '.', '*', '+', '?', '|',
      '(', ')', '[', ']', '{', '}', '\\'
    ];
	
    arguments.callee.sRE = new RegExp(
      '(\\' + specials.join('|\\') + ')', 'g'
    );
  }
  return text.replace(arguments.callee.sRE, '\\$1');
}

Function.prototype.bind = function() {
    if (arguments.length < 2 && typeof(arguments[0]) == 'undefined') return this;
	args = [];
	for (var i=0;i<arguments.length;i++) args.push(arguments[i]);
    var __method = this, object = args.shift();
    return function() {
      var argv = [];
	  for (var i=0;i<arguments.length;i++) argv.push(arguments[i]);
      return __method.apply(object, args.concat(argv));
    }
  };

  //Taken from prototype
Function.prototype.bindAsEventListener = function() {
	args = [];
	for (var i=0;i<arguments.length;i++) args.push(arguments[i]);
    var __method = this, object = args.shift();
    return function(event) {
      var argv = [];
	  for (var i=0;i<arguments.length;i++) argv.push(arguments[i]);
      return __method.apply(object, [event || window.event].concat(args).concat(argv));
    }
  };
 if (DEBUG) console.log('End utils.js');